module oderogue.tcod.BSPTree;

import derelict.tcod.libtcod;

import oderogue.tcod.Random;

public alias TCOD_bsp_callback_t BSPTreeCallback;

public struct BSPTree
{
	private TCOD_bsp_t *bsp;
	
	public this(int x, int y, int w, int h)
	{
		bsp = TCOD_bsp_new_with_size(x, y, w, h);
	}
	
	private this(TCOD_bsp_t *bsp)
	{
		this.bsp = bsp;
	}
	
	public ~this()
	{
		dispose();
	}
	
	@property
	{
		public int x() { return bsp.x; }
		public int y() { return bsp.y; }
		public int width() { return bsp.w; }
		public int height() { return bsp.h; }
		public int position() { return bsp.position; }
		public int horizontal() { return bsp.horizontal; }
	}
	
	/**
	 * Delete the tree, including the root node to release every resource used.
	 */
	public void dispose()
	{
		if (bsp)
		{
			TCOD_bsp_delete(bsp);
			bsp = null;
		}
	}
	
	/**
	 * Delete a part of the tree, releasing resources for all sub nodes.
	 */
	public void removeChildren()
	{
		TCOD_bsp_remove_sons(bsp);
	}
	
	/**
	 * Split the node into two smaller non-overlapping nodes.
	 */
	public void splitOnce(bool horizontal, int position)
	{
		TCOD_bsp_split_once(bsp, horizontal, position);
	}
	
	/**
	 * Recursively split the bsp. At each step, a random orientation (horizontal/vertical) and position are choosen.
	 */
	public void splitRecursive(Random randomizer, int nb, int minHSize, int maxHRatio, int minVSize, 
							   int maxVRatio)
	{
		TCOD_bsp_split_recursive(bsp, randomizer.data, nb, minHSize, minVSize, maxHRatio, maxVRatio);
	}
	
	/**
	 * This operation resets the size of the tree nodes without changing the splitting data (orientation/position). 
	 * It should be called with the initial region size or a bigger size, else some splitting position may be out of 
	 * the region.
	 * You can use it if you changed the nodes size and position while using the BSP tree, which happens typically when 
	 * you use the tree to build a dungeon. You create rooms inside the tree leafs, then shrink the leaf to fit 
	 * the room size. Calling resize on the root node with the original region size allows you to reset all nodes to 
	 * their original size.
	 */
	public void resize(int x, int y, int w, int h)
	{
		TCOD_bsp_resize(bsp, x, y, w, h);
	}
	
	public BSPTree getParent()
	{
		return BSPTree(TCOD_bsp_father(bsp));
	}
	
	public BSPTree getRight()
	{
		return BSPTree(TCOD_bsp_right(bsp));
	}
	
	public BSPTree getLeft()
	{
		return BSPTree(TCOD_bsp_left(bsp));
	}	
	
	public bool isLeaf()
	{
		return TCOD_bsp_is_leaf(bsp);
	}
	
	/**
	 * Check if a map cell is inside a node.
	 */
	public bool contains(int x, int y)
	{
		return TCOD_bsp_contains(bsp, x, y);
	}
	
	/**
	 * Search the tree for the smallest node containing a map cell. If the cell is outside the tree, 
	 * the function returns null.
	 */
	public BSPTree findNode(int x, int y)
	{
		return BSPTree(TCOD_bsp_find_node(bsp, x, y));
	}
	
	public bool traversePreOrder(BSPTreeCallback callback, void* data)
	{
		return TCOD_bsp_traverse_pre_order(bsp, callback, data);
	}
	
	public bool traverseInOrder(BSPTreeCallback callback, void* data)
	{
		return TCOD_bsp_traverse_in_order(bsp, callback, data);
	}
	
	public bool traversePostOrder(BSPTreeCallback callback, void* data)
	{
		return TCOD_bsp_traverse_post_order(bsp, callback, data);
	}
	
	public bool traverseLevelOrder(BSPTreeCallback callback, void* data)
	{
		return TCOD_bsp_traverse_level_order(bsp, callback, data);
	}
	
	public bool traverseInvertedLevelOrder(BSPTreeCallback callback, void* data)
	{
		return TCOD_bsp_traverse_inverted_level_order(bsp, callback, data);
	}
}